package com.example.designpattern.creational.singleton.lazy.double_check_lock;

/**
 * 懒汉式：先声明一个空变量，需要用时才初始化。
 * <p>
 * 我们先声明了一个初始值为 null 的 instance 变量，当需要使用时判断此变量是否已被初始化，没有初始化的话才 new 一个实例出来。
 * 就好比电工在修理灯泡时，开始比较偷懒，什么工具都不拿，当发现需要使用螺丝刀时，才把螺丝刀拿出来。
 * 当需要用钳子时，再把钳子拿出来。就像一个不到万不得已不会行动的懒汉，所以称之为懒汉式。
 * <p>
 * 懒汉式解决了饿汉式的弊端，好处是按需加载，避免了内存浪费，减少了类初始化时间。
 */
public class Singleton {

//    private static Singleton instance = null;
    // 防止指令重排序
    private volatile static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {

//        if (instance == null) {
//            instance = new Singleton();
//        }

        // 上述代码的懒汉式单例乍一看没什么问题，但其实它不是线程安全的。
        // 如果有多个线程同一时间调用 getInstance 方法，instance 变量可能会被实例化多次。
        // 为了保证线程安全，我们需要给判空过程加上锁
//        synchronized (Singleton.class) {
//            if (instance == null) {
//                instance = new Singleton();
//            }
//        }

        // 这样就能保证多个线程调用 getInstance 时，一次最多只有一个线程能够执行判空并 new 出实例的操作，
        // 所以 instance 只会实例化一次。但这样的写法仍然有问题，当多个线程调用 getInstance 时，
        // 每次都需要执行 synchronized 同步化方法，这样会严重影响程序的执行效率。所以更好的做法是在同步化之前，再加上一层检查：
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }

        // 这样增加一种检查方式后，如果 instance 已经被实例化，则不会执行同步化操作，大大提升了程序效率。
        // 上面这种写法也就是我们平时较常用的双检锁方式实现的线程安全的单例模式。
        // 但这样的懒汉式单例仍然有一个问题，JVM 底层为了优化程序运行效率，可能会对我们的代码进行指令重排序，
        // 在一些特殊情况下会导致出现空指针，为了防止这个问题，更进一步的优化是给 instance 变量加上 volatile 关键字。


        // 有读者可能会有疑问，我们在外面检查了 instance == null, 那么锁里面的空检查是否可以去掉呢？
        // 答案是不可以。如果里面不做空检查，可能会有两个线程同时通过了外面的空检查，然后在一个线程 new 出实例后，
        // 第二个线程进入锁中又 new 出一个实例，导致创建多个实例。


        return instance;
    }
}